<?php

/**
 * This file is part of the Framework03 PHP framework.
 * @author Walter Tamboer
 * @package Framework03
 */

class FW_Http_Response
{
    /**
     * @var int The HTTP response code of this response.
     */
    private $code;

    /**
     * @var array The HTTP headers of this response.
     */
    private $headers;

    /**
     * @var string The HTTP body of this response.
     */
    private $body;

    /**
     * @var int The HTTP version of this response.
     */
    private $version;

    /**
     * @var int The HTTP message of this response.
     */
    private $message;

    /**
     * Initializes a new instance of this class.
     * @param int $code
     * @param array $headers
     * @param string $body
     * @param string $version
     * @param string $message
     */
    public function __construct($code, array $headers, $body, $version, $message)
    {
        $this->code = $code;
        $this->headers = $headers;
        $this->body = $body;
        $this->version = $version;
        $this->message = $message;
    }

    /**
     * Gets the code of the HTTP response.
     * @reutrn int
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Gets the headers of the HTTP response.
     * @reutrn array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * Gets the body of the HTTP response.
     * @reutrn string
     */
    public function getBody()
    {
        return $this->body;
    }

    /**
     * Gets the version of the HTTP response.
     * @reutrn string
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * Gets the message of the HTTP response.
     * @reutrn string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Checks whether or not the response is an error.
     * @return boolean
     */
    public function isError()
    {
        $type = floor($this->code / 100);

        return ($type == 4 || $type == 5);
    }

    /**
     * Check whether the response in successful
     *
     * @return boolean
     */
    public function isSuccessful()
    {
        $type = floor($this->code / 100);

        return ($type == 2 || $type == 1);
    }

    /**
     * Check whether the response is a redirection
     *
     * @return boolean
     */
    public function isRedirect()
    {
        $type = floor($this->code / 100);
        
        return ($type == 3);
    }

    /**
     * Creates a new FW_Http_Response object from the string.
     * @param string $response
     * @return FW_Http_Response
     */
    public static function fromString($response)
    {
        $code = self::extractCode($response);
        $headers = self::extractHeaders($response);
        $body = self::extractBody($response);
        $version = self::extractVersion($response);
        $message = self::extractMessage($response);

        return new FW_Http_Response($code, $headers, $body, $version, $message);
    }

    /**
     * Extracts the response code from the given response string.
     * @param string $response
     * @return int
     */
    public static function extractCode($response)
    {
        preg_match('/^HTTP\/[\d\.x]+ (\d+)/', $response, $matches);

        if (isset($matches[1]))
            return (int)$matches[1];
        
        return false;
    }

    /**
     * Extracts the HTTP message from the given response
     * @param string $response
     * @return string
     */
    public static function extractMessage($response)
    {
        preg_match("/^HTTP\/[\d\.x]+ \d+ ([^\r\n]+)/", $response, $matches);

        if (isset($matches[1]))
            return $matches[1];

        return false;
    }

    /**
     * Extracts the HTTP version from the given response.
     * @param string $response
     * @return string
     */
    public static function extractVersion($response)
    {
        preg_match("/^HTTP\/([\d\.x]+) \d+/", $response, $matches);

        if (isset($matches[1]))
            return $matches[1];

        return false;
    }

    /**
     * Extracts the headers from the given response string
     * @param string $response
     * @return array
     */
    public static function extractHeaders($response)
    {
        $headers = array();

        // First, split body and headers
        $parts = preg_split('/(?:\r?\n){2}/m', $response, 2);
        if (!$parts[0]) return $headers;

        // Split headers part to lines
        $lines = explode("\n", $parts[0]);
        unset($parts);
        $last_header = null;

        foreach($lines as $line)
        {
            $line = trim($line, "\r\n");
            if ($line == "") break;
            
            if (preg_match("|^([\w-]+):\s+(.+)|", $line, $matches))
            {
                unset($last_header);
                $name = strtolower($matches[1]);
                $value = $matches[2];

                if (isset($headers[$name]))
                {
                    if (!is_array($headers[$name]))
                        $headers[$name] = array($headers[$name]);
                    
                    $headers[$name][] = $value;
                }
                else
                    $headers[$name] = $value;

                $last_header = $name;
            }
            else if (preg_match("|^\s+(.+)$|", $line, $matches) && $last_header !== null)
            {
                if (is_array($headers[$last_header]))
                {
                    end($headers[$last_header]);
                    $last_header_key = key($headers[$last_header]);
                    $headers[$last_header][$last_header_key] .= $matches[1];
                }
                else
                    $headers[$last_header] .= $matches[1];
            }
        }

        return $headers;
    }

    /**
     * Extracts the body from the given response string.
     * @param string $response
     * @return string
     */
    public static function extractBody($response)
    {
        $parts = preg_split('/(?:\r?\n){2}/m', $response, 2);

        if (isset($parts[1]))
            return $parts[1];
            
        return '';
    }
}
